/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename: util.cc
 */

/* Cross-platform portability code and standard includes */
#include <global.h>
/* Contains system-wide constants and #defines */
#include <definitions.h>

#include <m_string.h>
#include <db_static.h>
#include <db_pthread.h>
#include <my_alloc.h>
#include <configmake.h>
#include <libclient/password.h>
#include <util.h>
#include <clpsar_sys_err.h>

#include <decimal.h>

extern pthread_mutex_t     THR_LOCK_open;
#include <zlib.h>

#ifdef HAVE_PWD_H
#include <pwd.h>
#endif

#include <errno.h>
#ifdef HAVE_REALPATH
#include <sys/param.h>
#include <sys/stat.h>
#endif

#if defined(HAVE_UTIME_H)
#include <utime.h>
#elif defined(HAVE_SYS_UTIME_H)
#include <sys/utime.h>
#elif !defined(HPUX10)
#include <time.h>
struct utimbuf {
  time_t actime;
  time_t modtime;
};
#endif

#include <util/test.h>


char wild_many  ='%';
char wild_one   ='_';
char wild_prefix= '\\';

#ifdef __cplusplus
extern "C" {
#endif

 char *util_octet2hex(char *to, const char *str, uint32_t len)
{
  static const char u_dig_vec_upper[]= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  const char *str_end= str + len;
  for (; str != str_end; ++str)
  {
    *to++= u_dig_vec_upper[((unsigned char) *str) >> 4];
    *to++= u_dig_vec_upper[((unsigned char) *str) & 0x0F];
  }
  *to= '\0';
  return to;
}


/*
  New random generation structure initialization
  SYNOPSIS
    util_randominit()
    rand_st    OUT  Structure to initialize
    seed1      IN   First initialization parameter
    seed2      IN   Second initialization parameter
*/

void util_randominit(struct rand_struct *rand_st,
                                  uint64_t seed1,
                                  uint64_t seed2)
{
  memset(rand_st, 0, sizeof(*rand_st));/* Avoid UMC varnings */
  rand_st->max_value= 0x3FFFFFFFL;
  rand_st->max_value_dbl=(double) rand_st->max_value;
  rand_st->seed1=seed1%rand_st->max_value ;
  rand_st->seed2=seed2%rand_st->max_value;
}


/*
  Formats a filename with possible replace of directory of extension
  Function can handle the case where 'to' == 'name'
  For a description of the flag values,
  The arguments should be in unix format.
*/

char * fn_format    (char * to,
                     const char *name,
                     const char *dir,
                     const char *extension,
                     uint32_t flag)
{
  char dev[FN_REFLEN], buff[FN_REFLEN], *pos;
  const char *startpos = name;
  const char *ext;
  register size_t length;
  size_t dev_length;

  /* Copy and skip directory */
  name+=(length=dirname_part(dev, startpos, &dev_length));
  if (length == 0 || (flag & MY_REPLACE_DIR))
  {
    /* Use given directory */
    convert_dirname(dev,dir,NULL); /* Fix to this OS */
  }
  else if ((flag & MY_RELATIVE_PATH) && !test_if_hard_path(dev))
  {
    /* Put 'dir' before the given path */
    strncpy(buff,dev,sizeof(buff)-1);
    pos=convert_dirname(dev,dir,NULL);
    strncpy(pos,buff,sizeof(buff)-1- (int) (pos-dev));
  }

  if (flag & MY_UNPACK_FILENAME)
    (void) unpack_dirname(dev,dev); /* Replace ~/.. with dir */
  if (!(flag & MY_APPEND_EXT) &&
      (pos= (char*) strchr(name,FN_EXTCHAR)) != NULL)
  {
    if ((flag & MY_REPLACE_EXT) == 0) /* If we should keep old ext */
    {
      length =strlength(name);  /* Use old extension */
      ext = "";
    }
    else
    {
      length= (size_t) (pos-(char*) name); /* Change extension */
      ext= extension;
    }
  }
  else
  {
    length=strlength(name); /* No ext, use the now one */
    ext=extension;
  }

  if (strlen(dev)+length+strlen(ext) >= FN_REFLEN || length >= FN_LEN )
  {
    /* To long path, return original or NULL */
    size_t tmp_length;
    if (flag & MY_SAFE_PATH)
      return NULL;
    tmp_length= cmin(strlength(startpos), FN_REFLEN-1);
    strncpy(to,startpos,tmp_length);
    to[tmp_length]= '\0';
  }
  else
  {
    if (to == startpos)
    {
      memmove(buff, name, length); /* Save name for last copy */
      name=buff;
    }
    char *tmp= strcpy(to, dev) + strlen(dev);
    pos= strncpy(tmp,name,length) + length;
    (void) strcpy(pos,ext); /* Don't convert extension */
  }
  /*
    If MY_RETURN_REAL_PATH and MY_RESOLVE_SYMLINK is given, only do
    realpath if the file is a symbolic link
  */
  if (flag & MY_RETURN_REAL_PATH)
    (void) db_realpath(to, to, MYF(flag & MY_RESOLVE_SYMLINKS ?
                              MY_RESOLVE_LINK: 0));
  else if (flag & MY_RESOLVE_SYMLINKS)
  {
    strcpy(buff,to);
    (void) db_readlink(to, buff, MYF(0));
  }
  return(to);
} /* fn_format */




/*
  strlength(const string str)
  Return length of string with end-space:s not counted.
*/

size_t strlength(const char *str)
{
  register const char * pos;
  register const char * found;

  pos= found= str;
  while (*pos)
  {
    if (*pos != ' ')
    {
      while (*++pos && *pos != ' ') {};
      if (!*pos)
      {
         found=pos; /* String ends here */
         break;
      }
    }
    found=pos;
    while (*++pos == ' ') {};
  }
  return((size_t) (found - str));
} /* strlength */

size_t dirname_length(const char *name)
{
  register const char *pos, *gpos;
#ifdef BASKSLASH_MBTAIL
  CHARSET_INFO *fs= fs_character_set();
#endif
#ifdef FN_DEVCHAR
  if ((pos=(char*)strrchr(name,FN_DEVCHAR)) == 0)
#endif
    pos=name-1;

  gpos= pos++;
  for ( ; *pos ; pos++) /* Find last FN_LIBCHAR */
  {
#ifdef BASKSLASH_MBTAIL
    uint32_t l;
    if (use_mb(fs) && (l= my_ismbchar(fs, pos, pos + 3)))
    {
      pos+= l - 1;
      continue;
    }
#endif
    if (*pos == FN_LIBCHAR || *pos == '/'
#ifdef FN_C_AFTER_DIR
	|| *pos == FN_C_AFTER_DIR || *pos == FN_C_AFTER_DIR_2
#endif
	)
      gpos=pos;
  }
  return gpos-name+1;
}

/*
  Gives directory part of filename. Directory ends with '/'

  SYNOPSIS
    dirname_part()
    to          Store directory name here
    name        Original name
    to_length   Store length of 'to' here

  RETURN
   #  Length of directory part in 'name'
*/

size_t dirname_part(char *to, const char *name, size_t *to_res_length)
{
  size_t length;

  length        =  dirname_length(name);
  *to_res_length= (size_t) (convert_dirname(to, name, name+length) - to);
  return(length);
} /* dirname_part */


/*
  Convert directory name to use under this system

  SYNPOSIS
    convert_dirname()
    to              Store result here. Must be at least of size
                    cmin(FN_REFLEN, strlen(from) + 1) to make room
                    for adding FN_LIBCHAR at the end.
    from            Original filename. May be == to
    from_end        Pointer at end of filename (normally end \0)

  IMPLEMENTATION
    If MSDOS converts '/' to '\'
    If VMS converts '<' to '[' and '>' to ']'
    Adds a FN_LIBCHAR to end if the result string if there isn't one
    and the last isn't dev_char.
    Copies data from 'from' until ASCII(0) for until from == from_end
    If you want to use the whole 'from' string, just send NULL as the
    last argument.

    If the result string is larger than FN_REFLEN -1, then it's cut.

  RETURN
   Returns pointer to end \0 in to
*/

#ifndef FN_DEVCHAR
#define FN_DEVCHAR '\0'    /* For easier code */
#endif

char *convert_dirname(char *to, const char *from, const char *from_end)
{
  char *to_org=to;
#ifdef BACKSLASH_MBTAIL
  CHARSET_INFO *fs= fs_character_set();
#endif

  /* We use -2 here, because we need place for the last FN_LIBCHAR */
  if (!from_end || (from_end - from) > FN_REFLEN-2)
    from_end=from+FN_REFLEN -2;

#if FN_LIBCHAR != '/' || defined(FN_C_BEFORE_DIR_2)
  {
    for (; from != from_end && *from ; from++)
    {
      if (*from == '/')
          *to++= FN_LIBCHAR;
#ifdef FN_C_BEFORE_DIR_2
      else if (*from == FN_C_BEFORE_DIR_2)
               *to++= FN_C_BEFORE_DIR;
      else if (*from == FN_C_AFTER_DIR_2)
               *to++= FN_C_AFTER_DIR;
#endif
      else
      {
#ifdef BACKSLASH_MBTAIL
        uint32_t l;
        if (use_mb(fs) && (l= my_ismbchar(fs, from, from + 3)))
        {
          memmove(to, from, l);
          to+= l;
          from+= l - 1;
          to_org= to; /* Don't look inside mbchar */
        }
        else
#endif
        {
          *to++= *from;
        }
      }
    }
    *to=0;
  }
#else
  /* This is ok even if to == from, becasue we need to cut the string */
  size_t len= cmin(strlen(from),(size_t)(from_end-from));
  assert(memmove(to, from, len));
  to+= len;
  to[0]= '\0';
#endif

  /* Add FN_LIBCHAR to the end of directory path */
  if (to != to_org && (to[-1] != FN_LIBCHAR && to[-1] != FN_DEVCHAR))
  {
    *to++=FN_LIBCHAR;
    *to=0;
  }
  return(to);                              /* Pointer to end of dir */
} /* convert_dirname */

static char * expand_tilde(char * *path);


/*
  remove unwanted chars from dirname

  SYNOPSIS
     cleanup_dirname()
     to     Store result here
     from   Dirname to fix.  May be same as to

  IMPLEMENTATION
  "/../" removes prev dir
  "/~/" removes all before ~
  //" is same as "/", except on Win32 at start of a file
  "/./" is removed
  Unpacks home_dir if "~/.." used
  Unpacks current dir if if "./.." used

  RETURN
    #  length of new name
*/

size_t cleanup_dirname(register char *to, const char *from)
{
  register size_t length;
  register char * pos;
  register const char * from_ptr;
  register char * start;
  char parent[5], /* for "FN_PARENTDIR" */
       buff[FN_REFLEN+1],*end_parentdir;
#ifdef BACKSLASH_MBTAIL
  CHARSET_INFO *fs= fs_character_set();
#endif

  start=buff;
  from_ptr= from;
#ifdef FN_DEVCHAR
  if ((pos=strrchr((char*)from_ptr,FN_DEVCHAR)) != 0)
  {  /* Skip device part */
    length=(size_t) (pos-from_ptr)+1;
    start= strncpy(buff,from_ptr,length);
    start+= strlen(from_ptr);
    from_ptr+=length;
  }
#endif

  parent[0]=FN_LIBCHAR;
  length= (size_t)((strcpy(parent+1,FN_PARENTDIR)+strlen(FN_PARENTDIR))-parent);
  for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++)
  {
#ifdef BACKSLASH_MBTAIL
    uint32_t l;
    if (use_mb(fs) && (l= my_ismbchar(fs, from_ptr - 1, from_ptr + 2)))
    {
      for (l-- ; l ; *++pos= *from_ptr++, l--);
      start= pos + 1; /* Don't look inside multi-byte char */
      continue;
    }
#endif
    if (*pos == '/')
      *pos = FN_LIBCHAR;
    if (*pos == FN_LIBCHAR)
    {
      if ((size_t) (pos-start) > length &&
          memcmp(pos-length,parent,length) == 0)
      { /* If .../../; skip prev */
          pos-=length;
          if (pos != start)
         { /* not /../ */
          pos--;
           if (*pos == FN_HOMELIB && (pos == start || pos[-1] == FN_LIBCHAR))
          {
            if (!home_dir)
            {
              pos+=length+1; /* Don't unpack ~/.. */
              continue;
            }
            pos= strcpy(buff,home_dir)+strlen(home_dir)-1;	/* Unpacks ~/.. */
           if (*pos == FN_LIBCHAR)
               pos--;          /* home ended with '/' */
          }
            if (*pos == FN_CURLIB && (pos == start || pos[-1] == FN_LIBCHAR))
          {
            if (getcwd(curr_dir,FN_REFLEN))
            {
              pos+=length+1;  /* Don't unpack ./.. */
              continue;
            }
            pos= strcpy(buff,curr_dir)+strlen(curr_dir)-1;	/* Unpacks ./.. */
            if (*pos == FN_LIBCHAR)
             pos--;          /* home ended with '/' */
          }
          end_parentdir=pos;
          while (pos >= start && *pos != FN_LIBCHAR)	/* remove prev dir */
            pos--;
           if (pos[1] == FN_HOMELIB || memcmp(pos,parent,length) == 0)
          {                 /* Don't remove ~user/ */
            pos= strcpy(end_parentdir+1,parent)+strlen(parent);
            *pos=FN_LIBCHAR;
            continue;
          }
        }
      }
      else if ((size_t) (pos-start) == length-1 &&
           !memcmp(start,parent+1,length-1))
      start=pos;            /* Starts with "../" */
      else if (pos-start > 0 && pos[-1] == FN_LIBCHAR)
      {
#ifdef FN_NETWORK_DRIVES
	      if (pos-start != 1)
#endif
	      pos--;            /* Remove dupplicate '/' */
      }
      else if (pos-start > 1 && pos[-1] == FN_CURLIB && pos[-2] == FN_LIBCHAR)
         pos-=2;            /* Skip /./ */
      else if (pos > buff+1 && pos[-1] == FN_HOMELIB && pos[-2] == FN_LIBCHAR)
      {      /* Found ..../~/  */
         buff[0]=FN_HOMELIB;
         buff[1]=FN_LIBCHAR;
         start=buff; pos=buff+1;
      }
    }
  }
  (void) strcpy(to,buff);
  return((size_t) (pos-buff));
} /* cleanup_dirname */


/*
  On system where you don't have symbolic links, the following
  code will allow you to create a file:
  directory-name.sym that should contain the real path
  to the directory.  This will be used if the directory name
  doesn't exists
*/


bool my_use_symdir=0;  /* Set this if you want to use symdirs */

#ifdef USE_SYMDIR
void symdirget(char *dir)
{
  char buff[FN_REFLEN];
  char *pos= strchr(dir, '\0');
  if (dir[0] && pos[-1] != FN_DEVCHAR && my_access(dir, F_OK))
  {
    File file;
    size_t length;
    char temp= *(--pos);            /* May be "/" or "\" */
    strcpy(pos,".sym");
    file= my_open(dir, O_RDONLY, MYF(0));
    *pos++=temp; *pos=0;	  /* Restore old filename */
    if (file >= 0)
    {
      if ((length= my_read(file, buff, sizeof(buff), MYF(0))) > 0)
      {
        for (pos= buff + length ;
             pos > buff && (iscntrl(pos[-1]) || isspace(pos[-1])) ;
             pos --);

        /* Ensure that the symlink ends with the directory symbol */
        if (pos == buff || pos[-1] != FN_LIBCHAR)
          *pos++=FN_LIBCHAR;

        strncpy(dir,buff, FN_REFLEN-1);
      }
      db_close_file(file, MYF(0));
    }
  }
}
#endif /* USE_SYMDIR */


/*
  Fixes a directory name so that can be used by open()

  SYNOPSIS
    unpack_dirname()
    to           result-buffer, FN_REFLEN characters. may be == from
    from         'Packed' directory name (may contain ~)

 IMPLEMENTATION
  Make that last char of to is '/' if from not empty and
  from doesn't end in FN_DEVCHAR
  Uses cleanup_dirname and changes ~/.. to home_dir/..

  Changes a UNIX filename to system filename (replaces / with \ on windows)

  RETURN
   Length of new directory name (= length of to)
*/

size_t unpack_dirname(char * to, const char *from)
{
  size_t length, h_length;
  char buff[FN_REFLEN+1+4],*suffix,*tilde_expansion;

  (void) intern_filename(buff,from);      /* Change to intern name */
  length= strlen(buff);                   /* Fix that '/' is last */
  if (length &&
#ifdef FN_DEVCHAR
      buff[length-1] != FN_DEVCHAR &&
#endif
      buff[length-1] != FN_LIBCHAR && buff[length-1] != '/')
  {
    buff[length]=FN_LIBCHAR;
    buff[length+1]= '\0';
  }

  length=cleanup_dirname(buff,buff);
  if (buff[0] == FN_HOMELIB)
  {
    suffix=buff+1;
    tilde_expansion=expand_tilde(&suffix);
    if (tilde_expansion)
    {
      length-= (size_t) (suffix-buff)-1;
      if (length+(h_length= strlen(tilde_expansion)) <= FN_REFLEN)
      {
    if (tilde_expansion[h_length-1] == FN_LIBCHAR)
       h_length--;
    if (buff+h_length < suffix)
          memmove(buff+h_length, suffix, length);
    else
        bmove_upp((unsigned char*) buff+h_length+length,
               (unsigned char*) suffix+length, length);
        memmove(buff, tilde_expansion, h_length);
      }
    }
  }
#ifdef USE_SYMDIR
  if (my_use_symdir)
    symdirget(buff);
#endif
  return(system_filename(to,buff)); /* Fix for open */
} /* unpack_dirname */


    /* Expand tilde to home or user-directory */
    /* Path is reset to point at FN_LIBCHAR after ~xxx */

static char * expand_tilde(char * *path)
{
  if (path[0][0] == FN_LIBCHAR)
    return home_dir; /* ~/ expanded to home */
#ifdef HAVE_GETPWNAM
  {
    char *str,save;
    struct passwd *user_entry;

    if (!(str=strchr(*path,FN_LIBCHAR)))
      str= strchr(*path, '\0');
    save= *str; *str= '\0';
    user_entry=getpwnam(*path);
    *str=save;
    endpwent();
    if (user_entry)
    {
      *path=str;
      return user_entry->pw_dir;
    }
  }
#endif
  return NULL;
}


/*
  Fix filename so it can be used by open, create

  SYNOPSIS
    unpack_filename()
    to      Store result here. Must be at least of size FN_REFLEN.
    from    Filename in unix format (with ~)

  RETURN
    # length of to

  NOTES
    to may be == from
    ~ will only be expanded if total length < FN_REFLEN
*/


size_t unpack_filename(char * to, const char *from)
{
  size_t length, n_length, buff_length;
  char buff[FN_REFLEN];
  /* copy & convert dirname */
  length=dirname_part(buff, from, &buff_length);
  n_length=unpack_dirname(buff,buff);
  if (n_length+strlen(from+length) < FN_REFLEN)
  {
    (void) strcpy(buff+n_length,from+length);
    length= system_filename(to,buff); /* Fix to usably filename */
  }
  else
    length= system_filename(to,from); /* Fix to usably filename */
  return(length);
} /* unpack_filename */


 /* Convert filename (unix standard) to system standard */
 /* Used before system command's like open(), create() .. */
 /* Returns used length of to; total length should be FN_REFLEN */

size_t system_filename(char * to, const char *from)
{
#ifndef FN_C_BEFORE_DIR
  return strlen(strncpy(to,from,FN_REFLEN-1));
#else	/* VMS */

	/* change 'dev:lib/xxx' to 'dev:[lib]xxx' */
	/* change 'dev:xxx' to 'dev:xxx' */
	/* change './xxx' to 'xxx' */
	/* change './lib/' or lib/ to '[.lib]' */
	/* change '/x/y/z to '[x.y]x' */
	/* change 'dev:/x' to 'dev:[000000]x' */

  int libchar_found;
  size_t length;
  char * to_pos,from_pos,pos;
  char buff[FN_REFLEN];

  libchar_found=0;
  (void) strcpy(buff,from);			 /* If to == from */
  from_pos= buff;
  if ((pos=strrchr(from_pos,FN_DEVCHAR)))	/* Skip device part */
  {
    pos++;
    to_pos= strncpy(to,from_pos,(size_t) (pos-from_pos));
    to_pos+= strlen(to);
    from_pos=pos;
  }
  else
    to_pos=to;

  if (from_pos[0] == FN_CURLIB && from_pos[1] == FN_LIBCHAR)
    from_pos+=2; /* Skip './' */
  if (strchr(from_pos,FN_LIBCHAR))
  {
    *(to_pos++) = FN_C_BEFORE_DIR;
    if (strstr(from_pos,FN_ROOTDIR) == from_pos)
    {
      from_pos+=strlen(FN_ROOTDIR); /* Actually +1 but... */
      if (! strchr(from_pos,FN_LIBCHAR))
      { /* No dir, use [000000] */
        to_pos= strcpy(to_pos,FN_C_ROOT_DIR)+strlen(FN_C_ROOT_DIR);
        libchar_found++;
      }
    }
    else
      *(to_pos++)=FN_C_DIR_SEP; /* '.' gives current dir */

    while ((pos=strchr(from_pos,FN_LIBCHAR)))
    {
      if (libchar_found++)
        *(to_pos++)=FN_C_DIR_SEP; /* Add '.' between dirs */
      if (strstr(from_pos,FN_PARENTDIR) == from_pos &&
          from_pos+strlen(FN_PARENTDIR) == pos) {
        to_pos= strcpy(to_pos,FN_C_PARENT_DIR);	/* Found '../' */
        to_pos+= strlen(FN_C_PARENT_DIR);
      }
      else
      {
        to_pos= strncpy(to_pos,from_pos,(size_t) (pos-from_pos));
        to_pos+= strlen(to_pos);
      }
      from_pos=pos+1;
    }
    *(to_pos++)=FN_C_AFTER_DIR;
  }

  strcpy(to_pos, from_pos);
  length= strlen(to);
  return(length);
#endif
} /* system_filename */


	/* Fix a filename to intern (UNIX format) */

char *intern_filename(char *to, const char *from)
{
  size_t length, to_length;
  char buff[FN_REFLEN];
  if (from == to)
  { /* Dirname may destroy from */
    strcpy(buff,from);
    from=buff;
  }
  length= dirname_part(to, from, &to_length); /* Copy dirname & fix chars */
  (void) strcpy(to + to_length,from+length);
  return (to);
} /* intern_filename */



/*
  Reads the content of a symbolic link
  If the file is not a symbolic link, return the original file name in to.

  RETURN
    0  If filename was a symlink,    (to will be set to value of symlink)
    1  If filename was a normal file (to will be set to filename)
   -1  on error.
*/

int db_readlink(char *to, const char *filename, myf in_Flags)
{
#ifndef HAVE_READLINK
  strcpy(to,filename);
  return 1;
#else
  int result=0;
  int length;

  if ((length=readlink(filename, to, FN_REFLEN-1)) < 0)
  {
    /* Don't give an error if this wasn't a symlink */
    if ((my_errno=errno) == EINVAL)
    {
      result= 1;
      strcpy(to,filename);
    }
    else
    {
      if (in_Flags & MY_WME)
          my_error(EE_CANT_READLINK, MYF(0), filename, errno);
      result= -1;
    }
  }
  else
    to[length]=0;
  return(result);
#endif /* HAVE_READLINK */
}


/* Create a symbolic link */

int db_symlink(const char *content, const char *linkname, myf in_Flags)
{
#ifndef HAVE_READLINK
  return 0;
#else
  int result;

  result= 0;
  if (symlink(content, linkname))
  {
    result= -1;
    my_errno=errno;
    if (in_Flags & MY_WME)
      my_error(EE_CANT_SYMLINK, MYF(0), linkname, content, errno);
  }
  /*
  else if ((in_Flags & MY_SYNC_DIR) && my_sync_dir_by_file(linkname, in_Flags))
    result= -1;
  */
  return(result);
#endif /* HAVE_READLINK */
}

/*
  Resolve all symbolic links in path
  'to' may be equal to 'filename'

  Because purify gives a lot of UMR errors when using realpath(),
  this code is disabled when using purify.

  If MY_RESOLVE_LINK is given, only do realpath if the file is a link.
*/

#if defined(SCO)
#define BUFF_LEN 4097
#elif defined(MAXPATHLEN)
#define BUFF_LEN MAXPATHLEN
#else
#define BUFF_LEN FN_LEN
#endif

int db_realpath(char *to, const char *filename, myf in_Flags)
{
#if defined(HAVE_REALPATH) &&  !defined(HAVE_BROKEN_REALPATH)
  int result=0;
  char buff[BUFF_LEN];
  struct stat stat_buff;

  if (!(in_Flags & MY_RESOLVE_LINK) ||
      (!lstat(filename,&stat_buff) && S_ISLNK(stat_buff.st_mode)))
  {
    char *ptr;
    if ((ptr=realpath(filename,buff)))
    {
      strncpy(to,ptr,FN_REFLEN-1);
    }
    else
    {
      /*
      	Realpath didn't work;  Use db_load_path() which is a poor substitute
      	original name but will at least be able to resolve paths that starts
      	with '.'.
      */
      my_errno=errno;
      if (in_Flags & MY_WME)
	    my_error(EE_REALPATH, MYF(0), filename, my_errno);
      db_load_path(to, filename, NULL);
      result= -1;
    }
  }
  return(result);
#else
  db_load_path(to, filename, NULL);
  return 0;
#endif

}


	/* Returns full load-path for a file. to may be = path */
	/* if path is a hard-path return path */
	/* if path starts with home-dir return path */
	/* if path starts with current dir or parent-dir unpack path */
	/* if there is no path, prepend with own_path_prefix if given */
	/* else unpack path according to current dir */

char * db_load_path(char * to, const char *path,
                   const char *own_path_prefix)
{
  char buff[FN_REFLEN];
  int is_cur;

  if ((path[0] == FN_HOMELIB && path[1] == FN_LIBCHAR) ||
      test_if_hard_path(path))
    strcpy(buff,path);
  else if ((is_cur=(path[0] == FN_CURLIB && path[1] == FN_LIBCHAR)) ||
	        (is_prefix(path,FN_PARENTDIR)) ||   !own_path_prefix)

  {
    if (is_cur)
      is_cur=2;                    /* Remove current dir */
    if (! getcwd(buff,(uint32_t) (FN_REFLEN-strlen(path)+is_cur)))
      strcat(buff,path+is_cur);
    else
      strcpy(buff,path);           /* Return org file name */
  }
  else
    sprintf(buff,"%s%s",own_path_prefix,path);
  strcpy(to,buff);
  return(to);
} /* db_load_path */

/*
  Sync data in file to disk

  SYNOPSIS
    db_sync()
    fd              File descriptor to sync
    my_flags        Flags (now only MY_WME is supported)

  NOTE
    If file system supports its, only file data is synced, not inode data.

    MY_IGNORE_BADFD is useful when fd is "volatile" - not protected by a
    mutex. In this case by the time of fsync(), fd may be already closed by
    another thread, or even reassigned to a different file. With this flag -
    MY_IGNORE_BADFD - such a situation will not be considered an error.
    (which is correct behavior, if we know that the other thread synced the
    file before closing)

  RETURN
    0 ok
    -1 error
*/

int db_sync(File fd, myf _flags)
{
  int res;

  do
  {
#if defined(F_FULLFSYNC)
    /*
      In Mac OS X >= 10.3 this call is safer than fsync() (it forces the
      disk's cache and guarantees ordered writes).
    */
    if (!(res= fcntl(fd, F_FULLFSYNC, 0)))
      break; /* ok */
    /* Some file systems don't support F_FULLFSYNC and fail above: */
#endif
#if defined(HAVE_FDATASYNC)
    res= fdatasync(fd);
#elif defined(HAVE_FSYNC)
    res= fsync(fd);
#else
#error Cannot find a way to sync a file, durability in danger;
    res= 0; /* No sync (strange OS) */
#endif
  } while (res == -1 && errno == EINTR);

  if (res)
  {
    int er= errno;
    if (!(my_errno= er))
      my_errno= -1;                             /* Unknown error */
    if ((_flags & MY_IGNORE_BADFD) &&
        (er == EBADF || er == EINVAL || er == EROFS))
    {
      res= 0;
    }
    else if (_flags & MY_WME)
      my_error(EE_SYNC, MYF(ME_BELL+ME_WAITTANG), db_filename(fd), my_errno);
  }
  return(res);
} /* db_sync */

/*
  Get filename of file

  SYNOPSIS
    db_filename()
      fd	File descriptor
*/

char * db_filename(File fd)
{
  /*uint32_t   my_file_limit= MY_NFILE; */
  if ((uint32_t) fd >= (uint32_t) my_file_limit)
    return((char*) "UNKNOWN");
  if (fd >= 0 && my_file_info[fd].type != UNOPEN)
  {
    return(my_file_info[fd].name);
  }
  else
    return((char*) "UNOPENED");	/* Debug message */
}  /* db_filename(File fd)*/




static const char cur_dir_name[]= {FN_CURLIB, 0};
/*
  Force directory information to disk.

  SYNOPSIS
    my_sync_dir()
    dir_name             the name of the directory
    my_flags             flags (MY_WME etc)

  RETURN
    0 if ok, !=0 if error
*/
#ifdef NEED_EXPLICIT_SYNC_DIR
int db_sync_dir(const char *dir_name, myf my_flags)
{
  File dir_fd;
  int res= 0;
  const char *correct_dir_name;
  /* Sometimes the path does not contain an explicit directory */
  correct_dir_name= (dir_name[0] == 0) ? cur_dir_name : dir_name;
  /*
    Syncing a dir may give EINVAL on tmpfs on Linux, which is ok.
    EIO on the other hand is very important. Hence MY_IGNORE_BADFD.
  */
  if ((dir_fd= db_open_file(correct_dir_name, O_RDONLY, MYF(my_flags))) >= 0)
  {
    if (db_sync(dir_fd, MYF(my_flags | MY_IGNORE_BADFD)))
      res= 2;
    if (db_close_file(dir_fd, MYF(my_flags)))
      res= 3;
  }
  else
    res= 1;
  return(res);
}
#else
int db_sync_dir(const char *, myf)
{
  return 0;
}
#endif


/*
  Force directory information to disk.

  SYNOPSIS
    db_sync_dir_by_file()
    file_name            the name of a file in the directory
    _flags               flags (MY_WME etc)

  RETURN
    0 if ok, !=0 if error
*/
#ifdef NEED_EXPLICIT_SYNC_DIR
int db_sync_dir_by_file(const char *file_name, myf _flags)
{
  char dir_name[FN_REFLEN];
  size_t dir_name_length;
  dirname_part(dir_name, file_name, &dir_name_length);
  return db_sync_dir(dir_name, _flags);
}
#else
int db_sync_dir_by_file(const char *, myf)
{
  return 0;
}
#endif

/**************************end of sync ***********************************/
/*
  Read a chunk of bytes from a file with retry's if needed

  The parameters are:
    File descriptor
    Buffer to hold at least Count bytes
    Bytes to read
    Flags on what to do on error

    Return:
      -1 on error
      0  if flag has bits MY_NABP or MY_FNABP set
      N  number of bytes read.
*/

size_t db_read
(  File Filedes,
   unsigned char *Buffer,
   size_t Count,
   myf i_Flags
)
{
  size_t readbytes, save_count;
  save_count= Count;

  for (;;)
  {
    errno= 0; /* Linux doesn't reset this */
    if ((readbytes= read(Filedes, Buffer, Count)) != Count)
    {
      my_errno= errno ? errno : -1;
      if ((readbytes == 0 || (int) readbytes == -1) && errno == EINTR)
      {
        continue;                              /* Interrupted */
      }
      if (i_Flags & (MY_WME | MY_FAE | MY_FNABP))
      {
        if (readbytes == (size_t) -1)
          my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG),
                   db_filename(Filedes),my_errno);
        else if (i_Flags & (MY_NABP | MY_FNABP))
          my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG),
                   db_filename(Filedes),my_errno);
      }
      if (readbytes == (size_t) -1 ||
          ((i_Flags & (MY_FNABP | MY_NABP)) && !(i_Flags & MY_FULL_IO)))
        return(MY_FILE_ERROR);  /* Return with error */
      if (readbytes != (size_t) -1 && (i_Flags & MY_FULL_IO))
      {
        Buffer+= readbytes;
        Count-= readbytes;
        continue;
      }
    }

    if (i_Flags & (MY_NABP | MY_FNABP))
      readbytes= 0; /* Ok on read */
    else if (i_Flags & MY_FULL_IO)
      readbytes= save_count;
    break;
  }
  return(readbytes);
} /* db_read */



	/* Write a chunk of bytes to a file */

size_t db_write
(  int Filedes,
   const unsigned char *Buffer,
   size_t Count,
   myf i_Flags
)
{
  size_t writenbytes, written;
  uint32_t errors;
  errors=0; written=0;

  /* The behavior of write(fd, buf, 0) is not portable
     global.h
  */
  if (unlikely(!Count))
    return(0);

  for (;;)
  {
    if ((writenbytes= write(Filedes, Buffer, Count)) == Count)
      break;
    if (writenbytes != (size_t) -1)
    {/* Safeguard */
      written+=writenbytes;
      Buffer+=writenbytes;
      Count-=writenbytes;
    }
    my_errno=errno;
#ifndef NO_BACKGROUND
    if (my_thread_var->abort)
      i_Flags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */
    if ((my_errno == ENOSPC || my_errno == EDQUOT) &&
        (i_Flags & MY_WAIT_IF_FULL))
    {
      if (!(errors++ % MY_WAIT_GIVE_USER_A_MESSAGE))
           my_error(EE_DISK_FULL,MYF(ME_BELL | ME_NOREFRESH),
           db_filename(Filedes),my_errno,MY_WAIT_FOR_USER_TO_FIX_PANIC);
      sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC);
      continue;
    }

    if ((writenbytes == 0 || writenbytes == (size_t) -1))
    {
      if (my_errno == EINTR)
      {
        continue;                               /* Interrupted */
      }

      if (!writenbytes && !errors++) /* Retry once */
      {
        /* We may come here if the file quota is exeeded */
        errno=EFBIG; /* Assume this is the error */
        continue;
      }
    }
    else
      continue; /* Retry */
#endif
    if (i_Flags & (MY_NABP | MY_FNABP))
    {
      if (i_Flags & (MY_WME | MY_FAE | MY_FNABP))
      {
           my_error(EE_WRITE, MYF(ME_BELL+ME_WAITTANG),
           db_filename(Filedes),my_errno);
      }
      return(MY_FILE_ERROR);  /* Error on read */
    }
    else
      break;                 /* Return bytes written */
  }
  if (i_Flags & (MY_NABP | MY_FNABP))
    return(0);                /* Want only errors */
  return(writenbytes+written);
} /* db_write */

	/* On unix rename deletes to file if it exists */

int db_rename
( const char *from,
  const char *to,
  myf i_Flags
)
{
  int error = 0;

#if defined(HAVE_FILE_VERSIONS)
  { /* Check that there isn't a old file */
    int save_errno;
    MY_STAT my_stat_result;
    save_errno=my_errno;
    if (my_stat(to,&my_stat_result,MYF(0)))
    {
      my_errno=EEXIST;
      error= -1;
      if (MyFlags & MY_FAE+MY_WME)
         my_error(EE_LINK, MYF(ME_BELL+ME_WAITTANG),from,to,my_errno);
      return(error);
    }
    my_errno=save_errno;
  }
#endif
#if defined(HAVE_RENAME)
  if (rename(from,to))
#else
  if (link(from, to) || unlink(from))
#endif
  {
    my_errno=errno;
    error = -1;
    if (i_Flags & (MY_FAE+MY_WME))
      my_error(EE_LINK, MYF(ME_BELL+ME_WAITTANG),from,to,my_errno);
  }
  else if (i_Flags & MY_SYNC_DIR)
  {
#ifdef NEED_EXPLICIT_SYNC_DIR
    /* do only the needed amount of syncs: */
    char dir_from[FN_REFLEN], dir_to[FN_REFLEN];
    size_t dir_from_length, dir_to_length;
    dirname_part(dir_from, from, &dir_from_length);
    dirname_part(dir_to, to, &dir_to_length);
    if (db_sync_dir(dir_from, i_Flags) ||
        (strcmp(dir_from, dir_to) &&
         db_sync_dir(dir_to, i_Flags)))
      error= -1;
#endif
  }
  return(error);
} /* db_rename */



int db_delete_file
( const char *name,
  myf i_Flags
)
{
  int err;

  if ((err = unlink(name)) == -1)
  {
    my_errno=errno;
    if (i_Flags & (MY_FAE+MY_WME))
      my_error(EE_DELETE,MYF(ME_BELL+ME_WAITTANG+(i_Flags & ME_NOINPUT)),
                 name,errno);
  }
  else if ((i_Flags & MY_SYNC_DIR) &&
           db_sync_dir_by_file(name, i_Flags))
    err= -1;
  return(err);
} /* db_delete_file */



/*
  Calculate a long checksum for a memoryblock.

  SYNOPSIS
    db_checksum()
      crc       start value for crc
      pos       pointer to memory block
      length    length of the block
*/

ha_checksum db_checksum
( ha_checksum crc,
  const unsigned char *pos,
  size_t length
)
{
  return (ha_checksum)crc32((uint32_t)crc, pos, length);
}

/*
  set how many open files we want to be able to handle

  SYNOPSIS
    set_maximum_open_files()
    max_file_limit          Files to open

  NOTES
    The request may not fulfilled because of system limitations

  RETURN
    Files available to open.
    May be more or less than max_file_limit!
*/

#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)

#ifndef RLIM_INFINITY
#define RLIM_INFINITY ((uint32_t) 0xffffffff)
#endif

static uint64_t set_max_open_files(uint64_t max_file_limit)
{
  struct rlimit rlimit;
  rlim_t old_cur;

  if (!getrlimit(RLIMIT_NOFILE,&rlimit))
  {
    old_cur= rlimit.rlim_cur;
    if (rlimit.rlim_cur == RLIM_INFINITY)
      rlimit.rlim_cur = max_file_limit;
    if (rlimit.rlim_cur >= max_file_limit)
    {
      if (rlimit.rlim_cur > UINT32_MAX)
        return UINT32_MAX;
      else
        return((uint32_t)rlimit.rlim_cur);
    }
    rlimit.rlim_cur= rlimit.rlim_max= max_file_limit;
    if (setrlimit(RLIMIT_NOFILE, &rlimit))
      max_file_limit= (old_cur < UINT32_MAX) ? (uint32_t)old_cur : UINT32_MAX;
    else
    {
      rlimit.rlim_cur= 0; /* Safety if next call fails */
      (void) getrlimit(RLIMIT_NOFILE,&rlimit);
      if (rlimit.rlim_cur) /* If call didn't fail */
	max_file_limit= (uint32_t) rlimit.rlim_cur;
    }
  }
  return(max_file_limit);
}

#else
static int set_max_open_files(uint64_t max_file_limit)
{
  /* We don't know the limit. Return best guess */
  return cmin(max_file_limit, OS_FILE_LIMIT);
}
#endif


/*
  Change number of open files

  SYNOPSIS:
    my_set_max_open_files()
    files                  Number of requested files

  RETURN
    number of files available for open
*/

uint64_t db_set_max_open_files(uint64_t files)
{
  struct st_my_file_info *tmp;

  files= set_max_open_files(cmin(files, OS_FILE_LIMIT));
  if (files <=  CLPSAR_NFILE)
    return(files);

  if (!(tmp= (st_my_file_info*) malloc((size_t)cmax(sizeof(st_my_file_info) * files,SIZE_MAX))))
    return(CLPSAR_NFILE);

  /* Copy any initialized files */
  memcpy(tmp, my_file_info,
         sizeof(*tmp)*(size_t)cmin(my_file_limit,
         cmax(files,UINT32_MAX)));
  /*
    The int cast is necessary since 'my_file_limits' might be greater
    than 'files'.
  */
  memset(tmp + my_file_limit, 0,
         cmax((int) (files - my_file_limit), 0)*sizeof(*tmp));
  db_free_open_file_info();      /* Free if already allocated */
  my_file_info= tmp;
  my_file_limit= (size_t)cmax(UINT32_MAX,files);
  return(files);
}



void db_free_open_file_info()
{
  if (my_file_info != file_info_default)
  {
    /* Copy data back for my_print_open_files */
    memcpy(file_info_default, my_file_info,
           sizeof(*file_info_default)* CLPSAR_NFILE);
    free((char*) my_file_info);
    my_file_info= file_info_default;
    my_file_limit= CLPSAR_NFILE;
  }
  return;
}


void free_defaults(char **argv)
{
  MEM_ROOT ptr;
  memcpy(&ptr, (char*) argv - sizeof(ptr), sizeof(ptr));
  free_root(&ptr,MYF(0));
}


/*
  Return a pointer to the extension of the filename.

  SYNOPSIS
    fn_ext()
    name        Name of file

  DESCRIPTION
    The extension is defined as everything after the first extension character
    (normally '.') after the directory name.

  RETURN VALUES
    Pointer to to the extension character. If there isn't any extension,
    points at the end ASCII(0) of the filename.
*/

char *fn_ext(const char *name)
{
  register const char *pos, *gpos;

#if defined(FN_DEVCHAR) || defined(FN_C_AFTER_DIR) || defined(BASKSLASH_MBTAIL)
  {
    char buff[FN_REFLEN];
    size_t res_length;
    gpos= name+ dirname_part(buff,(char*) name, &res_length);
  }
#else
  if (!(gpos= strrchr(name, FN_LIBCHAR)))
    gpos= name;
#endif
  pos=strchr(gpos,FN_EXTCHAR);
  return((char*) (pos ? pos : strchr(gpos, '\0')));
} /* fn_ext */



/*
Copy directory and/or extension between filenames.
(For the meaning of 'flag', check mf_format.c)
'to' may be equal to 'name'.
Returns 'to'.
*/

char * fn_same(char *to, const char *name, int flag)
{
  char dev[FN_REFLEN];
  const char *ext;
  size_t dev_length;

  if ((ext=strrchr(name+dirname_part(dev, name, &dev_length),FN_EXTCHAR)) == 0)
  ext="";

  return(fn_format(to,to,dev,ext,flag));
} /* fn_same */


/*
** Create a new file
** Arguments:
** Path-name of file
** Read | write on file (umask value)
** Read & Write on open file
** Special flags
*/


File db_create_file(const char *FileName, int CreateFlags, int access_flags,
                     myf _Flags)
{
  int fd, rc;

#if !defined(NO_OPEN_3)
fd = ::open(FileName, access_flags | O_CREAT,
      CreateFlags ? CreateFlags : db_umask);
#else
fd = ::open(FileName, access_flags);
#endif

  if ((_Flags & MY_SYNC_DIR) && (fd >=0) &&
      db_sync_dir_by_file(FileName, _Flags))
{
  db_close_file(fd, _Flags);
  fd= -1;
}

  rc= db_register_filename(fd, FileName, FILE_BY_CREATE,
                            EE_CANTCREATEFILE, _Flags);
/*
db_register_filename() may fail on some platforms even if the call to
*open() above succeeds. In this case, don't leave the stale file because
callers assume the file to not exist if db_create_file() fails, so they don't
do any cleanups.
*/
  if (unlikely(fd >= 0 && rc < 0))
{
    int tmp= my_errno;
    db_delete_file(FileName, _Flags);
    my_errno= tmp;
}
    return(rc);
} /* db_create_file */
/*
  Open a file

  SYNOPSIS
    db_open_file()
      FileName  Fully qualified file name
      Flags     Read | write
      _Flags    Special flags

  RETURN VALUE
    File descriptor
*/

File db_open_file(const char *FileName, int Flags, myf _Flags)
                /* Path-name of file */
                /* Read | write .. */
                /* Special flags */
{
  File fd;

#if !defined(NO_OPEN_3)
  fd = open(FileName, Flags, db_umask);/* Normal unix */
#else
  fd = open((char *) FileName, Flags);
#endif

  return(db_register_filename(fd, FileName, FILE_BY_OPEN,
                              EE_FILENOTFOUND, _Flags));
} /* db_open_file */


/*
  Close a file

  SYNOPSIS
    db_close_file()
      fd    File sescriptor
      myf   Special Flags

*/

int db_close_file(File fd, myf _Flags)
{
  int err;

  pthread_mutex_lock(&THR_LOCK_open);
  do
  {
    err= close(fd);
  } while (err == -1 && errno == EINTR);

  if (err)
  {
    my_errno=errno;
    if (_Flags & (MY_FAE | MY_WME))
      my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG),db_filename(fd),errno);
  }
  if ((uint32_t) fd < my_file_limit && my_file_info[fd].type != UNOPEN)
  {
    free(my_file_info[fd].name);
#if !defined(HAVE_PREAD)
    pthread_mutex_destroy(&my_file_info[fd].mutex);
#endif
    my_file_info[fd].type = UNOPEN;
  }
  g_file_opened--;
  pthread_mutex_unlock(&THR_LOCK_open);
  return(err);
} /* db_close_file */


/*
  Register file in my_file_info[]

  SYNOPSIS
    db_register_filename()
    fd                 File number opened, -1 if error on open
    FileName           File name
    type_file_type	   How file was created
    error_message_number   Error message number if caller got error (fd == -1)
    _Flags                 Flags for my_close()

  RETURN
    -1   error
     #   Filenumber

*/

File db_register_filename(File fd, const char *FileName, enum file_type
                          type_of_file, uint32_t error_message_number,
                          myf _Flags)
{
  if ((int) fd >= 0)
  {
    if ((uint32_t) fd >= my_file_limit)
    {
#if !defined(HAVE_PREAD)
      my_errno= EMFILE;
#else
      thread_safe_increment(g_file_opened,&THR_LOCK_open);
      return(fd);           /* safeguard */
#endif
    }
    else
    {
      pthread_mutex_lock(&THR_LOCK_open);
      if ((my_file_info[fd].name = (char*) strdup(FileName)))
      {
        g_file_opened++;
        g_file_total_opened++;
        my_file_info[fd].type = type_of_file;
#if !defined(HAVE_PREAD)
        pthread_mutex_init(&my_file_info[fd].mutex,MY_MUTEX_INIT_FAST);
#endif
        pthread_mutex_unlock(&THR_LOCK_open);
        return(fd);
      }
      pthread_mutex_unlock(&THR_LOCK_open);
      my_errno= ENOMEM;
    }
    (void) db_close_file(fd, _Flags);
  }
  else
    my_errno= errno;

  if (_Flags & (MY_FFNF | MY_FAE | MY_WME))
  {
    if (my_errno == EMFILE)
      error_message_number= EE_OUT_OF_FILERESOURCES;
    my_error(error_message_number, MYF(ME_BELL+ME_WAITTANG),
             FileName, my_errno);
  }
  return(-1);
}


#ifdef EXTRA_DEBUG

void db_print_open_files(void)
{
  if (g_file_opened | g_stream_opened)
  {
    uint32_t i;
    for (i= 0 ; i < my_file_limit ; i++)
    {
      if (my_file_info[i].type != UNOPEN)
      {
        fprintf(stderr, EE(EE_FILE_NOT_CLOSED), my_file_info[i].name, i);
        fputc('\n', stderr);
      }
    }
  }
}

#endif


const char *get_relative_path(const char *path , const char* prefix)
{
  if (test_if_hard_path(path) &&
      is_prefix(path,prefix) &&
      strcmp(prefix,FN_ROOTDIR))
  {
    if (strlen(PREFIX) < strlen(path))
        path+=(size_t) strlen(prefix);
    while (*path == FN_LIBCHAR)
      path++;
  }
  return path;
}

/*
  @brief
  Create a temporary file with unique name in a given directory

  @details
  create_temp_file
    to             pointer to buffer where temporary filename will be stored
    dir            directory where to create the file
    prefix         prefix the filename with this
    _Flags        Magic flags

  @return
    File descriptor of opened file if success
    -1 and sets errno if fails.

  @note
    The behavior of this function differs a lot between
    implementation, it's main use is to generate a file with
    a name that does not already exist.

    The implementation using mkstemp should be considered the
    reference implementation when adding a new or modifying an
    existing one

*/

File create_temp_file(char *to, const char *dir, const char *prefix,
                      myf MyFlags)
{
  File file= -1;

#if defined(_ZTC__)
  if (!dir)
    dir=getenv("TMPDIR");
  if ((res=tempnam((char*) dir,(char *) prefix)))
  {
    strncpy(to,res,FN_REFLEN-1);
    (*free)(res);
    file=db_create_file(to, 0, mode | O_EXCL, MyFlags);
  }
#elif defined(HAVE_MKSTEMP)
  {
    File org_file;
    std::string prefix_str;

    prefix_str= prefix ? prefix : "tmp.";
    prefix_str.append("XXXXXX");

    if (!dir && ! (dir =getenv("TMPDIR")))
      dir= P_tmpdir;
    if (strlen(dir)+prefix_str.length() > FN_REFLEN-2)
    {
      errno=my_errno= ENAMETOOLONG;
      return(file);
    }
    strcpy(convert_dirname(to,dir,NULL),prefix_str.c_str());
    org_file=mkstemp(to);
    /* TODO: This was old behavior, but really don't we want to
     * unlink files immediately under all circumstances?
     * if (mode & O_TEMPORARY)
      (void) my_delete(to, MYF(MY_WME | ME_NOINPUT));
     */
    file=db_register_filename(org_file, to, FILE_BY_MKSTEMP,
                              EE_CANTCREATEFILE, MyFlags);
    /* If we didn't manage to register the name, remove the temp file */
    if (org_file >= 0 && file < 0)
    {
      int tmp=my_errno;
      close(org_file);
      (void) db_delete_file(to, MYF(MY_WME | ME_NOINPUT));
      my_errno=tmp;
    }
  }
#elif defined(HAVE_TEMPNAM)
  {
    (void)MyFlags;
    char *res,**old_env,*temp_env[1];
    if (dir && !dir[0])
    { /* Change empty string to current dir */
      to[0]= FN_CURLIB;
      to[1]= 0;
      dir=to;
    }
    old_env= (char**) environ;
    if (dir)
    {           /* Don't use TMPDIR if dir is given */
      environ=(const char**) temp_env;
      temp_env[0]=0;
    }
    if ((res=tempnam((char*) dir, (char*) prefix)))
    {
      strncpy(to,res,FN_REFLEN-1);
      (*free)(res);
      file=db_create_file(to,0,
                          (int) (O_RDWR | O_TRUNC | O_EXCL),
                           MYF(MY_WME));

    }
    environ=(const char**) old_env;
  }
#else
#error No implementation found for create_temp_file
#endif
  if (file >= 0)
    thread_safe_increment(g_tmp_file_created,&THR_LOCK_open);
  return(file);
}

/*
  int db_copy_file(const char *from, const char *to, myf _Flags)

  NOTES
    Ordinary ownership and access times are copied from 'from-file'
    If _Flags & MY_HOLD_ORIGINAL_MODES is set and to-file exists then
    the modes of to-file isn't changed
    If _Flags & MY_DONT_OVERWRITE_FILE is set, we will give an error
    if the file existed.

  WARNING
    Don't set MY_FNABP or MY_NABP bits on when calling this function !

  RETURN
    0   ok
    #   Error
*/

int db_copy_file(const char *from, const char *to, myf _Flags)
{
  uint32_t Count;
  bool new_file_stat= 0; /* 1 if we could stat "to" */
  int create_flag;
  File from_file,to_file;
  unsigned char buff[IO_SIZE];
  struct stat stat_buff,new_stat_buff;

  from_file=to_file= -1;
  assert(!(_Flags & (MY_FNABP | MY_NABP))); /* for my_read/my_write */
  if (_Flags & MY_HOLD_ORIGINAL_MODES)      /* Copy stat if possible */
    new_file_stat= test(!stat((char*) to, &new_stat_buff));

  if ((from_file=db_open_file(from,O_RDONLY,_Flags)) >= 0)
  {
    if (stat(from, &stat_buff))
    {
      my_errno=errno;
      goto err;
    }
    if (_Flags & MY_HOLD_ORIGINAL_MODES && new_file_stat)
      stat_buff=new_stat_buff;
    create_flag= (_Flags & MY_DONT_OVERWRITE_FILE) ? O_EXCL : O_TRUNC;

    if ((to_file=  db_create_file(to,(int) stat_buff.st_mode,
                                  O_WRONLY | create_flag,_Flags)) < 0)
      goto err;

    while ((Count=db_read(from_file, buff, sizeof(buff), _Flags)) != 0)
    {
      if (Count == (uint32_t) -1 ||
          db_write(to_file,buff,Count,MYF(_Flags|MY_NABP)))
      goto err;
    }

    if (db_close_file(from_file,_Flags) | db_close_file(to_file,_Flags))
      return(-1);                        /* Error on close */

    /* Copy modes if possible */
    if (_Flags & MY_HOLD_ORIGINAL_MODES && !new_file_stat)
      return(0);    /* File copied but not stat */
    chmod(to, stat_buff.st_mode & 07777); /* Copy modes */
    if(chown(to, stat_buff.st_uid,stat_buff.st_gid)!=0)
        return 0;
    if (_Flags & MY_COPYTIME)
    {
      struct utimbuf timep;
      timep.actime  = stat_buff.st_atime;
      timep.modtime = stat_buff.st_mtime;
      utime((char*) to, &timep); /* last accessed and modified times */
    }
    return(0);
  }

err:
  if (from_file >= 0) db_close_file(from_file,_Flags);
  if (to_file >= 0)
  {
    db_close_file(to_file, _Flags);
    /* attempt to delete the to-file we've partially written */
    db_delete_file(to, _Flags);
  }
  return(-1);
} /* db_copy_file */

/*
  If the file was a symlink, delete both symlink and the file which the
  symlink pointed to.
*/

int db_delete_file_with_symlink(const char *name, myf Flags)
{
  char link_name[FN_REFLEN];
  int was_symlink= (!my_disable_symlinks &&
                    !db_readlink(link_name, name, MYF(0)));
  int result;

  if (!(result=db_delete_file(name, Flags)))
  {
    if (was_symlink)
      result=db_delete_file(link_name, Flags);
  }
  return(result);
}


int wild_compare(register const char *str,
                 register const char *wildstr,
                 bool str_is_pattern)
{
  char cmp;

  while (*wildstr)
  {
    while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
    {
      if (*wildstr == wild_prefix && wildstr[1])
      {
        wildstr++;
        if (str_is_pattern && *str++ != wild_prefix)
          return(1);
      }
      if (*wildstr++ != *str++)
        return(1);
    }
    if (! *wildstr )
      return(*str != 0);
    if (*wildstr++ == wild_one)
    {
      if (! *str || (str_is_pattern && *str == wild_many))
        return(1);                     /* One char; skip */
      if (*str++ == wild_prefix && str_is_pattern && *str)
        str++;
    }
    else
    {  /* Found '*' */
      while (str_is_pattern && *str == wild_many)
        str++;
      for (; *wildstr ==  wild_many || *wildstr == wild_one; wildstr++)
        if (*wildstr == wild_many)
        {
          while (str_is_pattern && *str == wild_many)
            str++;
        }
        else
        {
          if (str_is_pattern && *str == wild_prefix && str[1])
            str+=2;
          else if (! *str++)
            return (1);
        }
      if (!*wildstr)
        return(0);           /* '*' as last char: OK */
      if ((cmp= *wildstr) == wild_prefix && wildstr[1] && !str_is_pattern)
        cmp=wildstr[1];
      for (;;str++)
      {
        while (*str && *str != cmp)
          str++;
        if (!*str)
          return (1);
	if (wild_compare(str,wildstr,str_is_pattern) == 0)
          return (0);
      }
      /* We will never come here */
    }
  }
  return (*str != 0);
} /* wild_compare */

/*
** functions to change a double or float to a sortable string
** The following should work for IEEE
*/

#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)

void change_double_for_sort(double nr,unsigned char *to)
{
  unsigned char *tmp=(unsigned char*) to;
  if (nr == 0.0)
  {    /* Change to zero string */
    tmp[0]=(unsigned char) 128;
    memset(tmp+1, 0, sizeof(nr)-1);
  }
  else
  {
#ifdef WORDS_BIGENDIAN
    memcpy(tmp,&nr,sizeof(nr));
#else
    {
      unsigned char *ptr= (unsigned char*) &nr;
#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN)
      tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
      tmp[4]= ptr[7]; tmp[5]=ptr[6]; tmp[6]= ptr[5]; tmp[7]=ptr[4];
#else
      tmp[0]= ptr[7]; tmp[1]=ptr[6]; tmp[2]= ptr[5]; tmp[3]=ptr[4];
      tmp[4]= ptr[3]; tmp[5]=ptr[2]; tmp[6]= ptr[1]; tmp[7]=ptr[0];
#endif
    }
#endif
    if (tmp[0] & 128)       /* Negative */
    {                       /* make complement */
      uint32_t i;
      for (i=0 ; i < sizeof(nr); i++)
          tmp[i]=tmp[i] ^ (unsigned char) 255;
    }
    else
    { /* Set high and move exponent one up */
      uint16_t exp_part=(((uint16_t) tmp[0] << 8) | (uint16_t) tmp[1] |
                          (uint16_t) 32768);
      exp_part+= (uint16_t) 1 << (16-1-DBL_EXP_DIG);
      tmp[0]= (unsigned char) (exp_part >> 8);
      tmp[1]= (unsigned char) exp_part;
    }
  }
}

void string_ptr_sort(unsigned char *base, uint32_t items, size_t size)
{
#if INT_MAX > 65536L
  unsigned char **ptr= NULL;

  if (size <= 20 && items >= 1000 && items < 100000 &&
      (ptr= (unsigned char**) malloc(items*sizeof(char*))))
  {
    radixsort_for_str_ptr((unsigned char**) base,items,size,ptr);
    free((unsigned char*) ptr);
  }
  else
#endif
  {
    if (size && items)
    {
      my_qsort2(base,items, sizeof(unsigned char*), get_ptr_compare(size),
                (void*) &size);
    }
  }
}

/* Radixsort */

void radixsort_for_str_ptr(unsigned char **base, uint32_t number_of_elements,
                           size_t size_of_element, unsigned char **buffer)
{
  unsigned char **end,**ptr,**buffer_ptr;
  uint32_t *count_ptr,*count_end,count[256];
  int pass;

  end=base+number_of_elements; count_end=count+256;
  for (pass=(int) size_of_element-1 ; pass >= 0 ; pass--)
{
  memset(count, 0, sizeof(uint32_t)*256);
  for (ptr= base ; ptr < end ; ptr++)
    count[ptr[0][pass]]++;
  if (count[0] == number_of_elements)
    goto next;
  for (count_ptr=count+1 ; count_ptr < count_end ; count_ptr++)
{
  if (*count_ptr == number_of_elements)
     goto next;
  (*count_ptr)+= *(count_ptr-1);
}
  for (ptr= end ; ptr-- != base ;)
    buffer[--count[ptr[0][pass]]]= *ptr;
  for (ptr=base, buffer_ptr=buffer ; ptr < end ;)
  (*ptr++) = *buffer_ptr++;
next:;
}
}
static int ptr_compare(size_t *compare_length, unsigned char **a, unsigned char **b);
static int ptr_compare_0(size_t *compare_length, unsigned char **a, unsigned char **b);
static int ptr_compare_1(size_t *compare_length, unsigned char **a, unsigned char **b);
static int ptr_compare_2(size_t *compare_length, unsigned char **a, unsigned char **b);
static int ptr_compare_3(size_t *compare_length, unsigned char **a, unsigned char **b);


/* Get a pointer to a optimal byte-compare function for a given size */

qsort2_cmp get_ptr_compare (size_t size)
{
  if (size < 4)
    return (qsort2_cmp) ptr_compare;
  switch (size & 3) {
  case 0: return (qsort2_cmp) ptr_compare_0;
  case 1: return (qsort2_cmp) ptr_compare_1;
  case 2: return (qsort2_cmp) ptr_compare_2;
  case 3: return (qsort2_cmp) ptr_compare_3;
  }
  return 0; /* Impossible */
}


/*
  Compare to keys to see witch is smaller.
  Loop unrolled to make it quick !!
*/

#define cmp(N) if (first[N] != last[N]) return (int) first[N] - (int) last[N]

static int ptr_compare(size_t *compare_length, unsigned char **a, unsigned char **b)
{
  register int length= *compare_length;
  register unsigned char *first,*last;

  first= *a; last= *b;
  while (--length)
{
  if (*first++ != *last++)
    return (int) first[-1] - (int) last[-1];
}
  return (int) first[0] - (int) last[0];
}


static int ptr_compare_0(size_t *compare_length,unsigned char **a, unsigned char **b)
{
  register int length= *compare_length;
  register unsigned char *first,*last;

  first= *a; last= *b;
loop:
  cmp(0);
  cmp(1);
  cmp(2);
  cmp(3);
  if ((length-=4))
{
  first+=4;
  last+=4;
  goto loop;
}
return (0);
}


static int ptr_compare_1(size_t *compare_length,unsigned char **a, unsigned char **b)
{
  register int length= *compare_length-1;
  register unsigned char *first,*last;

  first= *a+1; last= *b+1;
  cmp(-1);
loop:
  cmp(0);
  cmp(1);
  cmp(2);
  cmp(3);
  if ((length-=4))
{
  first+=4;
  last+=4;
  goto loop;
}
  return (0);
}

static int ptr_compare_2(size_t *compare_length,unsigned char **a, unsigned char **b)
{
  register int length= *compare_length-2;
  register unsigned char *first,*last;

  first= *a +2 ; last= *b +2;
  cmp(-2);
  cmp(-1);
loop:
  cmp(0);
  cmp(1);
  cmp(2);
  cmp(3);
  if ((length-=4))
{
  first+=4;
  last+=4;
  goto loop;
}
return (0);
}

static int ptr_compare_3(size_t *compare_length,unsigned char **a, unsigned char **b)
{
  register int length= *compare_length-3;
  register unsigned char *first,*last;

  first= *a +3 ; last= *b +3;
  cmp(-3);
  cmp(-2);
  cmp(-1);
loop:
  cmp(0);
  cmp(1);
  cmp(2);
  cmp(3);
  if ((length-=4))
{
  first+=4;
  last+=4;
  goto loop;
}
return (0);
}


clpsar_off_t db_get_ptr(unsigned char *ptr, size_t pack_length)
{
  clpsar_off_t pos;
  switch (pack_length) {
#if SIZEOF_OFF_T > 4
  case 8: pos= (clpsar_off_t) mi_uint8korr(ptr); break;
  case 7: pos= (clpsar_off_t) mi_uint7korr(ptr); break;
  case 6: pos= (clpsar_off_t) mi_uint6korr(ptr); break;
  case 5: pos= (clpsar_off_t) mi_uint5korr(ptr); break;
#endif
  case 4: pos= (clpsar_off_t) mi_uint4korr(ptr); break;
  case 3: pos= (clpsar_off_t) mi_uint3korr(ptr); break;
  case 2: pos= (clpsar_off_t) mi_uint2korr(ptr); break;
  case 1: pos= (clpsar_off_t) *(unsigned char*) ptr; break;
  default: assert(0); return 0;
}
  return pos;
}



void db_store_ptr(unsigned char *buff, size_t pack_length, clpsar_off_t pos)
{
  switch (pack_length) {
#if SIZEOF_OFF_T > 4
  case 8: mi_int8store(buff,pos); break;
  case 7: mi_int7store(buff,pos); break;
  case 6: mi_int6store(buff,pos); break;
  case 5: mi_int5store(buff,pos); break;
#endif
  case 4: mi_int4store(buff,pos); break;
  case 3: mi_int3store(buff,pos); break;
  case 2: mi_int2store(buff,pos); break;
  case 1: buff[0]= (unsigned char) pos; break;
  default: assert(0);
  }
  return;
}


#ifdef __cplusplus
}
#endif


